package com.xinke.module.system.service.user;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.google.common.collect.Lists;
import com.xinke.component.common.enums.CommonStatusEnum;
import com.xinke.component.common.util.sno.IdGen;
import com.xinke.module.system.model.vo.user.MenuTreeVO;
import com.xinke.module.system.convert.menu.MenuConvert;
import com.xinke.module.system.redis.RedisKeyConstants;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.xinke.module.system.model.vo.user.*;
import com.xinke.module.system.model.entity.user.MenuDO;
import com.xinke.component.common.pojo.PageResult;

import com.xinke.module.system.mapper.user.MenuMapper;

import static com.xinke.component.common.exception.util.ServiceExceptionUtil.exception;
import static com.xinke.component.common.util.collection.CollectionUtils.convertList;
import static com.xinke.component.common.util.collection.CollectionUtils.convertMap;
import static com.xinke.module.system.model.entity.user.MenuDO.ID_ROOT;

/**
 * 菜单权限 Service 实现类
 *
 * @author yzx
 */
@Service
@Validated
public class MenuServiceImpl implements MenuService {

    @Resource
    private MenuMapper menuMapper;

    @Override
    public Long createMenu(MenuTreeVO createReqVO) {
        // 插入
        MenuDO menu = MenuConvert.convertVoToDo(createReqVO);
        if(Objects.isNull(menu.getMenuId())){
            menu.setMenuId(IdGen.nextId());
        }
        menuMapper.insert(menu);
        // 返回
        return menu.getMenuId();
    }

    @Override
    public void updateMenu(MenuTreeVO updateReqVO) {
        // 校验存在
        validateMenuExists(updateReqVO.getId());
        MenuDO menu = MenuConvert.convertVoToDo(updateReqVO);
        menuMapper.updateById(menu);
    }

    @Override
    public void deleteMenu(Long menuId) {
        // 校验存在
        validateMenuExists(menuId);
        // 删除
        menuMapper.deleteById(menuId);
    }

    private void validateMenuExists(Long menuId) {
        if (menuMapper.selectById(menuId) == null) {
            throw exception(null);
        }
    }

    @Override
    public MenuDO getMenu(Long menuId) {
        return menuMapper.selectById(menuId);
    }

    @Override
    public PageResult<MenuDO> getMenuPage(MenuPageReqVO pageReqVO) {
        return menuMapper.selectPage(pageReqVO);
    }

    @Override
    public List<MenuDO> getMenuList() {
        return menuMapper.selectList();
    }
    @Override
    public List<MenuDO> getMenuList(Collection<Long> ids) {
        // 当 ids 为空时，返回一个空的实例对象
        if (CollUtil.isEmpty(ids)) {
            return Lists.newArrayList();
        }
        return menuMapper.selectBatchIds(ids);
    }
    @Override
    public List<MenuDO> filterDisableMenus(List<MenuDO> menuList) {
        if (CollUtil.isEmpty(menuList)){
            return Collections.emptyList();
        }
        Map<Long, MenuDO> menuMap = convertMap(menuList, MenuDO::getMenuId);

        // 遍历 menu 菜单，查找不是禁用的菜单，添加到 enabledMenus 结果
        List<MenuDO> enabledMenus = new ArrayList<>();
        Set<Long> disabledMenuCache = new HashSet<>(); // 存下递归搜索过被禁用的菜单，防止重复的搜索
        for (MenuDO menu : menuList) {
            if (isMenuDisabled(menu, menuMap, disabledMenuCache)) {
                continue;
            }
            enabledMenus.add(menu);
        }
        return enabledMenus;
    }

    private boolean isMenuDisabled(MenuDO node, Map<Long, MenuDO> menuMap, Set<Long> disabledMenuCache) {
        // 如果已经判定是禁用的节点，直接结束
        if (disabledMenuCache.contains(node.getMenuId())) {
            return true;
        }

        // 1. 先判断自身是否禁用
        if (CommonStatusEnum.isDisable(node.getMenuStatus())) {
            disabledMenuCache.add(node.getMenuId());
            return true;
        }

        // 2. 遍历到 parentId 为根节点，则无需判断
        Long parentId = node.getMenuParentId();
        if (ObjUtil.equal(parentId, ID_ROOT)) {
            return false;
        }

        // 3. 继续遍历 parent 节点
        MenuDO parent = menuMap.get(parentId);
        if (parent == null || isMenuDisabled(parent, menuMap, disabledMenuCache)) {
            disabledMenuCache.add(node.getMenuId());
            return true;
        }
        return false;
    }

    @Override
    @Cacheable(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#permission")
    public List<Long> getMenuIdListByPermissionFromCache(String permission) {
        List<MenuDO> menus = menuMapper.selectListByPermission(permission);
        return convertList(menus, MenuDO::getMenuId);
    }

    @Override
    public List<MenuTreeVO> getMemuTreeList() {
        List<MenuDO> menuDOS = menuMapper.selectList();
        return convertToTree(menuDOS);
    }
    // 转换入口方法
    public List<MenuTreeVO> convertToTree(List<MenuDO> menuDOList) {
        // 1. DO转VO（包含meta转换）
        List<MenuTreeVO> allNodes = convertDOListToVOList(menuDOList);

        // 2. 构建树形结构
        return buildMenuTree(allNodes);
    }
    // 转换单个DO到VO（包含meta处理）
    private MenuTreeVO convertDOToVO(MenuDO menuDO) {
        MenuTreeVO vo = new MenuTreeVO();
        // 基础字段映射
        vo.setId(menuDO.getMenuId());
        vo.setPid(menuDO.getMenuParentId());
        vo.setAuthCode(menuDO.getMenuAuthCode());
        vo.setComponent(menuDO.getMenuComponent());
        vo.setName(menuDO.getMenuName());
        vo.setPath(menuDO.getMenuPath());
        vo.setRedirect(menuDO.getMenuRedirect());
        vo.setType(menuDO.getMenuType());
        vo.setStatus(menuDO.getMenuStatus());
        // 构建Meta对象
        MenuTreeVO.MenuMetaVO meta = vo.getMeta();
        meta.setActiveIcon(menuDO.getMetaActiveIcon());
        meta.setActivePath(menuDO.getMetaActivePath());
        meta.setAffixTab(menuDO.getMetaAffixTab());
        meta.setAffixTabOrder(menuDO.getMetaAffixTabOrder());
        meta.setBadge(menuDO.getMetaBadge());
        meta.setBadgeType(menuDO.getMetaBadgeType());
        meta.setBadgeVariants(menuDO.getMetaBadgeVariants());
        meta.setHideChildrenInMenu(menuDO.getMetaHideChildrenInMenu());
        meta.setHideInBreadcrumb(menuDO.getMetaHideInBreadcrumb());
        meta.setHideInMenu(menuDO.getMetaHideInMenu());
        meta.setHideInTab(menuDO.getMetaHideInTab());
        meta.setIcon(menuDO.getMetaIcon());
        meta.setIframeSrc(menuDO.getMetaIframeSrc());
        meta.setKeepAlive(menuDO.getMetaKeepAlive());
        meta.setLink(menuDO.getMetaLink());
        meta.setMaxNumOfOpenTab(menuDO.getMetaMaxNumOfOpenTab());
        meta.setNoBasicLayout(menuDO.getMetaNoBasicLayout());
        meta.setOpenInNewWindow(menuDO.getMetaOpenInNewWindow());
        meta.setOrder(menuDO.getMetaMenuOrder());
        meta.setQuery(menuDO.getMetaQuery());
        meta.setTitle(menuDO.getMetaTitle());
        vo.setMeta(meta);
        return vo;
    }

    // 批量转换方法
    private List<MenuTreeVO> convertDOListToVOList(List<MenuDO> menuDOList) {
        return menuDOList.stream()
                .map(this::convertDOToVO)
                .collect(Collectors.toList());
    }

    // 构建树形结构核心方法
    private List<MenuTreeVO> buildMenuTree(List<MenuTreeVO> allNodes) {
        // 使用Map存储所有节点（快速查找）
        Map<Long, MenuTreeVO> nodeMap = allNodes.stream()
                .collect(Collectors.toMap(MenuTreeVO::getId, Function.identity()));

        // 最终返回的根节点列表
        List<MenuTreeVO> rootNodes = new ArrayList<>();

        allNodes.forEach(node -> {
            Long pid = node.getPid();
            if (pid == 0) {  // 根节点
                rootNodes.add(node);
            } else {        // 子节点
                MenuTreeVO parent = nodeMap.get(pid);
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(node);
                }
            }
        });

        // 排序处理（如果需要）
        rootNodes.sort(Comparator.comparingInt(n -> n.getMeta().getOrder()));
        return rootNodes;
    }
}